﻿using System;
using System.Text;
using System.Collections.Generic;

using BizElements.Core.ProviderComponents;

namespace BizElements.Core
{
    #region Documentation.
    /// <summary>
    /// Represents an SQL aggregate function.
    /// </summary>
    /// <remarks><para>Aggregate functions perform a calculation on a set of values and return a single value. With 
    /// the exception of COUNT, aggregate functions ignore null values. Aggregate functions are often used with 
    /// the GROUP BY clause of the SELECT statement. Aggregate functions are allowed as expressions only in the 
    /// select list of a SELECT statement (either a subquery or an outer query) and a HAVING clause.</para>
    /// <para>AggregateFunction cannot be created by using its constructor. 
    /// Use <see cref="AggregateFunctionFactory"/> to create supported aggregate functions.</para>
    /// <seealso cref="SelectStatement"/></remarks>
    /// <example>
    /// The following example retrieves the number of employees hired before January 1, 2000.
    /// <code>
    /// public int CountEmployeesHiredBefore2000()
    /// {
    ///		// AggregateFunctionFactory is used to create an instance of an AggregateFunction.
    ///		AggregateFunction count = AggregateFunctionFactory.Count("hiredBefore2000Count");
    ///		
    /// 	EmployeesMeta employees = new EmployeesMeta();
    /// 	SelectStatement select = new SelectStatement(employees);
    /// 	select.SelectList.Add(count);
    /// 	select.Where.Add(PredicateFactory.Compare(employees.HireDate, "&lt;", new DateTime(2000, 1, 1)));
    /// 
    /// 	// Execute method returns a DataTable that contains one row.
    /// 	// Alias given to the aggregate function is the name of the column which holds the function result.
    /// 	DataTable data = select.Execute();
    /// 	int hiredBefore2000Count = (int) data.Rows[0]["hiredBefore2000Count"];
    /// 	return hiredBefore2000Count;
    /// }
    /// </code>
    /// </example>	
    #endregion
    [Serializable]
    public sealed class AggregateFunction : Function
    {
        #region Internal CTor.

        /// <summary>
        /// Initializes an aggregate function.
        /// </summary>
        /// <param name="functionName">Function name.</param>
        /// <param name="distinct">Specifies whether the function eliminates duplicate values.</param>
        /// <param name="alias">Alias. Often used when a function is used in a select list.</param>
        /// <param name="functionParameters">Function parameters.</param>
        /// <param name="returnType">Type of the value returned by function.</param>
        internal AggregateFunction(string functionName, bool distinct, string alias, IList<IRenderSql> functionParameters, Type returnType)
            : base(functionName, alias, functionParameters, returnType)
        {
            this.Distinct = distinct;
        }

        #endregion

        #region Internal properties.

        /// <summary>
        /// The DISTINCT keyword is optional with SUM, AVG, and COUNT. When DISTINCT is used, 
        /// duplicate values are eliminated before the sum, average, or count is calculated. 
        /// If you use DISTINCT, the expression must consist of a column name only. 
        /// It cannot include an arithmetic expression.
        /// </summary>
        internal bool Distinct { get; private set; }

        #endregion

        #region Render.

        /// <summary>
        /// Renders function as SQL statement.
        /// </summary>
        /// <param name="dbms">Target DBMS.</param>
        /// <param name="output">StringBuilder to which SQL is appended.</param>
        /// <param name="parameters">SQL parameter collection to which the object's and its children's
        /// parameters are added. After the rendering is done the collection contains all parameters with unique names.</param>
        public override void Render(DbmsType dbms, StringBuilder output, DbParameterCollection parameters)
        {
            IAggregateFunctionRenderer renderer = DbmsComponentFactory.GetComponent<IAggregateFunctionRenderer>(dbms);
            renderer.Render(this, dbms, output, parameters);
        }

        #endregion
    }
}